#pragma once

#include <iostream>
#include <string>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/wait.h>
#include<sys/resource.h>

#include"../Com/Log.hpp"
#include"../Com/Util.hpp"

namespace ns_run 
{
    using namespace ns_LOG;
    using namespace ns_Util;    
    class Run 
    {
    public:
        Run() {}
        ~Run() {}

        static void SetLimit(int cpu_limit, int mem_limit)
        {
            //设置CPU时间限制
            struct rlimit rlim;
            rlim.rlim_cur = cpu_limit;
            rlim.rlim_max = RLIM_INFINITY;
            setrlimit(RLIMIT_CPU, &rlim);
            
            //设置内存限制
            rlim.rlim_cur = mem_limit * 1024;
            rlim.rlim_max = RLIM_INFINITY;
            setrlimit(RLIMIT_AS, &rlim);
        }
        //指明文件名即可，不用带路径和后缀
        /***************
         * run返回值：
         * 1.<0：运行出错
         * 2.0：运行成功，程序正常退出
         * 3.>0：运行成功，程序非正常退出
         * 
         * cpu_limit：CPU时间限制，单位为秒
         * mem_limit：内存限制，单位为kb
         * 
         * *******************/
        static int run(const std::string &file_name, int cpu_limit, int mem_limit)
        {
            /************
             * 对于程序运行结果：
             * 1.代码跑完，结果正确
             * 2.代码跑完，结果不正确
             * 3.代码没跑完，异常
             * 
             * 对于本模块不需要考虑运行结果正确与否，只考虑是否正确运行完毕
             * 
             * 而对于一个程序启动时：
             * 1.标准输入：不作处理
             * 2.标准输出：输出运行结果
             * 3.标准错误：输出运行过程中的错误信息
             * 
             * 将以上三种情况都写进文件中
             * ************/
            
            std::string execute = PathUtil::EXE(file_name);
            std::string _stdin = PathUtil::STDIN(file_name);
            std::string _stdout = PathUtil::STDOUT(file_name);
            std::string _stderr = PathUtil::STDERR(file_name);

            umask(0); 
            int _stdin_fd = open(_stdin.c_str(), O_CREAT | O_RDONLY,0644);
            int _stdout_fd = open(_stdout.c_str(), O_CREAT | O_WRONLY,0644);
            int _stderr_fd = open(_stderr.c_str(), O_CREAT | O_WRONLY,0644);

            if(_stdin_fd < 0 || _stdout_fd < 0 || _stderr_fd < 0)
            {
                LOG(ERROR) << "open file error" << "\n";
                return -1;
            }
            pid_t pid = fork();
            if(pid < 0)
            {
                close(_stdin_fd);
                close(_stdout_fd);
                close(_stderr_fd);
                LOG(ERROR) << "fork error" << "\n";
                return -2;
            }
            else if(pid == 0)
            {
                std::cout <<  cpu_limit << "   " << mem_limit << std::endl;
                //重定向到文件
                dup2(_stdin_fd, 0);
                dup2(_stdout_fd, 1);
                dup2(_stderr_fd, 2);
                //设置资源限制
                SetLimit(cpu_limit, mem_limit);

                // 使用execv代替execl，避免参数列表解析问题
                // char* args[] = {const_cast<char*>(execute.c_str()), NULL};
                
                // // 执行程序
                // execv(execute.data(), args);
                
                // // 如果execv返回，说明执行失败
                // LOG(ERROR) << "程序替换失败 (" << execute << "): " << strerror(errno) << "\n";
                // LOG(ERROR) << "错误代码: " << errno << " (" << strerror(errno) << ")\n";
                
                // // 输出调试信息：检查路径长度
                // LOG(ERROR) << "路径长度: " << execute.length() << " 字符\n";
                // exit(1);


                // std::cout << ".......................... " << std::endl;

                //程序替换
                // std::cout << execute << std::endl;

                execl(execute.c_str()/*执行程序的路径*/, execute.c_str()/*程序执行的方法*/, nullptr);
                // 如果execl返回，说明执行失败
                LOG(ERROR) << "程序替换失败 (" << execute << "): " << strerror(errno) << "\n";
                exit(1);
            }
            else
            {
                close(_stdin_fd);
                close(_stdout_fd);
                close(_stderr_fd);
                int status;
                waitpid(pid, &status, 0);
                LOG(INFO) << "program exit with status: " << (status & 0x7F) << "\n";
                return status & 0x7F;
            }
            
        }
    };
}
    